#! /usr/bin/python

'''
                   _ _     _           _   _                         
 ___ _ _ ___ ___ _| |_|___| |_ ___ ___| |_|_|___ ___   ___ ___ _____ 
| . | | |  _| -_| . | |_ -|  _| . |  _|  _| | . |   |_|  _| . |     |
|  _|___|_| |___|___|_|___|_| |___|_| |_| |_|___|_|_|_|___|___|_|_|_|
|_|                                                          

simpleDaemon.py - A simple damon module.
By Dale Stirling (dale [at] puredistortin [dot] com)

Copyright 2010 Dale Stirling. All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met:

   1. Redistributions of source code must retain the above copyright notice, this list of
      conditions and the following disclaimer.

   2. Redistributions in binary form must reproduce the above copyright notice, this list
      of conditions and the following disclaimer in the documentation and/or other materials
      provided with the distribution.

THIS SOFTWARE IS PROVIDED BY DALE STIRLING ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DALE STIRLING OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

The views and conclusions contained in the software and documentation are those of the
authors and should not be interpreted as representing official policies, either expressed
or implied, of Dale Stirling.
'''

# Modules
import sys
import os 
import signal 
import atexit

# Function for example below.
def main():
    """ A demo daemon main routine, write a datestamp to 
        /tmp/daemon-log every 10 seconds.
    """
    import time

    f = open("/tmp/daemon-log", "w") 
    while 1: 
        f.write('%s\n' % time.ctime(time.time())) 
        f.flush() 
        time.sleep(10)
        
def daemonize(daemon_home='/', create_pid='y', pid_path='/var/run/'):
    # do the UNIX double-fork magic, see Stevens' "Advanced 
    # Programming in the UNIX Environment" for details (ISBN 0201563177)
    try: 
        pid = os.fork() 
        if pid > 0:
            # exit first parent
            sys.exit(0) 
    except OSError, e: 
        print >>sys.stderr, "fork #1 failed: %d (%s)" % (e.errno, e.strerror) 
        sys.exit(1)

    # decouple from parent environment
    os.chdir(daemon_home) 
    os.setsid() 
    os.umask(0) 
    


    # do second fork
    try: 
		pid = os.fork() 
		if pid > 0:
			if create_pid == 'y':
 				scriptFilename, ext = os.path.splitext(os.path.basename(sys.argv[0]))
 				pidFile = '%s.pid' % (scriptFilename) 
 				pidFilePath = os.path.join(pid_path, pidFile)
 				f = file(pidFilePath, 'w') 
 				print >> f, pid
			sys.exit(0) 
			
    except OSError, e: 
        print >>sys.stderr, "fork #2 failed: %d (%s)" % (e.errno, e.strerror) 
        sys.exit(1)		

def exithandler():
	def systemexit(sig, freme):
		sys.exit()
	
	signal.signal(signal.SIGTERM, systemexit)

def cleanup(pid_path='/var/run/'):
	'''Function to cleanup the mess when the Daemon is given SIGTERM'''
	
	#Make sure all open file descriptiors have been closed.
	for n in range(3, 256):		
		try: 		
			os.close(n)
		except:
			pass
	
	#Make sure that we have closed stdin & stdout.
	sys.stdin.close()
	sys.stdout.close()
	
	# Remove the PID file just before we exit. 
	scriptFilename, ext = os.path.splitext(os.path.basename(sys.argv[0]))
	pidFile = '%s.pid' % (scriptFilename) 
	pidFilePath = os.path.join(pid_path, pidFile)
	os.remove(pidFilePath)
		
if __name__ == "__main__":

	print '''
                   _ _     _           _   _                         
 ___ _ _ ___ ___ _| |_|___| |_ ___ ___| |_|_|___ ___   ___ ___ _____ 
| . | | |  _| -_| . | |_ -|  _| . |  _|  _| | . |   |_|  _| . |     |
|  _|___|_| |___|___|_|___|_| |___|_| |_| |_|___|_|_|_|___|___|_|_|_|
|_|                                                          

simpleDaemon by Dale STirling (dale [at] puredistortion [dot] com )
Version: 1.0 2010-10-18

This is a reusable module to dameonize any python script quikly and simply.

In this example we create are writing to /tmp/daemon-log every 10 seconds 
with a date stamp. Also the script writes a pid file in the /tmp directory
as well. 

To terminate the script simply run the command `kill <process id>`. Process
id can be found in /tmp/<scriptname>.pid or using `ps aux | grep <script name>`
'''

#Now here is where the daemon begins.

	# This function forks our script and detaches it from the parent and creates
	# the daemon. Ensure the directories that you specify in the variables the 
	# user you are executing the daemon with has access.
	daemonize('/', 'y', '/tmp/')
	
	# This is demonstrating the pid file as you can see it uses the name of the script
	# as the file name. You can use the `ps` command to verify the accuracy of this.
	pidFile = file('/tmp/daemon_example.pid', 'r')
	print 'PID File:'
	for line in pidFile:
		print line
	pidFile.close()
	
	# This is so we can use atexit to register cleanup functions. I use at exit as 
	# it allows me to specify arguements to the function I am registering. This needs 
	# to be specified before you start registering functions with atexit()
	exithandler()
	
	# Once the 'exithandler()' function is called you can register all the functions 
	# that you need to run when the daemon exits. The only issue is that atexit will 
	# execute the functions in the REVERSE order of there registration.
	
	# Get ready to clean up the mess. The cleanup function specified here cleans up 
	# the PID file and all open file discriptiors ensuring that we exit nice and clean.
	atexit.register(cleanup, pid_path='/tmp/')
	
	# Starting the daemon main loop.
	main() 
